class DimReducer:
    
    def __init__(self, methodType, methodName):
        self._methodType = methodType        
        self._methodName = methodName
        self._reduceDim = getattr(self, "reduceDimBy%s"%self._methodName)    
    
    def filterByLowFrequency(self, globalWordFreqDist, threshold = 1):
        """
        filter out the words whose frequency is under 'threshold'
        @return: a set of words which have higher frequency
        """
        highFreqWordList = []  
        for word in globalWordFreqDist.keys():
            if globalWordFreqDist[word] > threshold:
                highFreqWordList.append(word)
        return highFreqWordList
    
    def reduceDim(self, trainingDataNodeDict, dimensionList, classList, threshold):
        """
        reduce dimensions
        @keyword trainingDataNodeDict: maintaining words' occurrence in each text
        @keyword dimensionList: a list dimensions to be pruned
        @keyword classList: list(pickedClass,'-1'), 2 categories of the text residing in 'trainingDataNodeDict'
        @keyword threshold: dimensions whose judged value is under this 'threshold' are omitted
        @return:  
        """
        reducedDimWordList = self._reduceDim(trainingDataNodeDict, dimensionList, classList, threshold)
        for dataNode in trainingDataNodeDict.values():
            dataNode.integrateTokens()
        return reducedDimWordList
        
    def resizeAndAttachClass(self, reducedDimSet, binaryClassList, dataNodeDict, srcFeatureMatrix):
        """
        reduce dimensions referring to reducedDimSet, meanwhile attach corresponding 'class' to each DataNode
        @keyword binaryClassList: [ pickedClass, '-1'] 
        """
        resizedMatrix = srcFeatureMatrix
        classDict = {}
        for nodeID in resizedMatrix.keys():
            for word in reducedDimSet:
                if word not in resizedMatrix[nodeID]:
                    del resizedMatrix[nodeID][word]        
            if binaryClassList[0] in (dataNodeDict[nodeID].classFreqDist.keys()):
                classDict[nodeID] = binaryClassList[0]
            else:
                classDict[nodeID] = binaryClassList[1]
        return (resizedMatrix, classDict)
        
    def reduceDimByNone(self, trainingDataNodeDict, dimensionList, classList, threshold):
        return dimensionList
        
    def reduceDimByInformationGain(self, dataNodeDict, dimensionList, binaryClassList, threshold):
        from math import log
        from nltk import FreqDist
        reducedDimWordList = []
        infoGainDict = FreqDist()
        N = len(dataNodeDict)
        for word in dimensionList:
            igValue = 0.0
            w = 0.0
            c = 0.0
            cw = 0.0
            infoGainDict[word] = 0.0
            for clz in binaryClassList:
                for dataNode in dataNodeDict.values():
                    if dataNode.wordsFreqDist[word] > 0:
                        w += 1
                    if clz in dataNode.classFreqDist.keys():
                        c += 1
                        if dataNode.wordsFreqDist[word] > 0:
                            cw += 1
                if c == 0:
                    igValue = 0
                    #pass
                else:
                    pc = c/N
                    pw = w/N
                    p_w = 1.0 - pw
                    pcw = cw/c
                    pc_w = 1 - pcw                  
                    if pc: 
                        igValue_0 = -pc*log(pc)
                    else:
                        igValue_0 = 0.0
                        
                    if pcw:
                        igValue_1 = pw*pcw*log(pcw)
                    else:
                        igValue_1 = 0.0
                    
                    if pc_w:
                        igValue_2 = p_w*pc_w*log(pc_w)
                    else:
                        igValue_2 = 0.0
                    
                    igValue = igValue_0 + igValue_1 + igValue_2                
                infoGainDict[word] += igValue
            # select the top 'threshold'num of words as attributes
            wordList = infoGainDict.sorted()
            reducedDimWordList = wordList[:threshold]
        print '%d -- %d reduced -> %d'%(len(dimensionList), len(dimensionList)-len(reducedDimWordList), len(reducedDimWordList))
        return reducedDimWordList
